package data

import (
	"context"
	"fmt"
	"shop-demo/app/order/service/internal/biz"
	"shop-demo/app/order/service/internal/data/ent"
	"shop-demo/app/order/service/internal/data/ent/order"
)

type orderRepo struct {
	data *Data
}

func NewOrderRepo(data *Data) biz.OrderRepo {
	return &orderRepo{data: data}
}

func (o *orderRepo) CreateOrder(ctx context.Context, order *biz.Order) (*biz.Order, error) {
	tx, err := o.data.db.Tx(ctx)
	if err != nil {
		return nil, err
	}
	orderMain, err := tx.Order.Create().
		SetUserID(order.UserId).
		Save(ctx)
	if err != nil {
		return nil, rollback(tx, err)
	}
	for _, detail := range order.Details {
		_, err := tx.OrderDetail.Create().
			SetProductID(detail.Id).
			SetProductName(detail.Name).
			SetProductNum(detail.Num).
			SetOwner(orderMain).
			Save(ctx)
		if err != nil {
			return nil, rollback(tx, err)
		}
	}

	if err := tx.Commit(); err != nil {
		return nil, rollback(tx, err)
	}

	return &biz.Order{
		Id:      orderMain.ID,
		Details: order.Details,
	}, nil
}

func rollback(tx *ent.Tx, err error) error {
	if rbErr := tx.Rollback(); rbErr != nil {
		err = fmt.Errorf("%w: %v", err, rbErr)
	}
	return err
}

func (o *orderRepo) GetOrder(ctx context.Context, id int64) (*biz.Order, error) {
	orderData, err := o.data.db.Order.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	orderDetails, err := orderData.QueryDetails().All(ctx)
	if err != nil {
		return nil, err
	}
	result := &biz.Order{
		Id:      orderData.ID,
		Details: make([]*biz.Product, 0, len(orderDetails)),
	}
	for _, detail := range orderDetails {
		result.Details = append(result.Details, &biz.Product{
			Id:   detail.ProductID,
			Name: detail.ProductName,
			Num:  detail.ProductNum,
		})
	}
	return result, nil
}

func (o *orderRepo) ListOrder(ctx context.Context, pageNum int64, pageSize int64) ([]*biz.Order, error) {
	orderSlice, err := o.data.db.Order.Query().WithDetails().Limit(int(pageSize)).Offset(int((pageNum - 1) * pageSize)).Order(ent.Desc(order.FieldID)).All(ctx)
	if err != nil {
		return nil, err
	}
	result := make([]*biz.Order, 0, len(orderSlice))
	for _, orderElem := range orderSlice {
		bizOrder := &biz.Order{
			Id:      0,
			Details: make([]*biz.Product, 0, len(orderElem.Edges.Details)),
		}
		result = append(result, bizOrder)
		for _, detail := range orderElem.Edges.Details {
			bizOrder.Details = append(bizOrder.Details, &biz.Product{
				Id:   detail.ProductID,
				Name: detail.ProductName,
				Num:  detail.ProductNum,
			})
		}
	}

	return result, nil
}
